package com.jiangruan.javaproject.address;

import com.jiangruan.javaproject.tools.Base58;
import com.jiangruan.javaproject.tools.Hash;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.security.*;
import java.security.interfaces.ECPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECPoint;
import java.util.Arrays;

/**
 * @author:liufukai
 * @email:1324963242@qq.com
 * @desc:该类用于推导生成比特币原始格式地址，并进行效验
 * @datatime:2021/12/228:56
 */
public class Addresss {
    /**
     * 该方法用于生成一对密钥对，并将该密钥对返回
     *
     * @return生成的密钥对
     */
    public KeyPair generateKey() {
        try {
            //指定获取密钥对的方法EC，密码学使用同一台api
            KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
            //EC算法中的参数
            ECGenParameterSpec spec = new ECGenParameterSpec("secp256k1");
            //初始化
            generator.initialize(spec);
            return generator.genKeyPair();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 该方法用于根据提供的keypair密钥信息，推导原始格式地址
     *
     * @param keyPair 密钥信息
     * @return 生产的原始格式地址
     */
    public String newAddress(KeyPair keyPair) {
        //先获取公钥在强转为ec公钥  对象转原始装公钥数据
        ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
       //自己进行原始格式公钥的拼接
        byte[] pubKey=toUnCompressPubKey(publicKey);
        //对公钥进行hash计算
        byte[] sha256= Hash.SHA256(pubKey);
        //ripemd160
        byte[] pubhash=Hash.RipeMD160(sha256);
        //添加版本号
        byte[] version=new byte[1];
        version[0]=0x00;
        byte[] ver_pubhash=new byte[version.length+pubhash.length];
        System.arraycopy(version,0,ver_pubhash,0,version.length);
        System.arraycopy(pubhash,0,ver_pubhash,1,pubhash.length);
        //双hash，取前四个字节
        byte[] hash1 = Hash.SHA256(ver_pubhash);
        byte[] hash2 = Hash.SHA256(hash1);
        //效验位数组
        byte[] check = new byte[4];
        System.arraycopy(hash2,0,check,0,check.length);

        //申请ver_pubhash_check数组
        byte[] ver_pubhash_check = new byte[ver_pubhash.length +check.length];
        System.arraycopy(ver_pubhash,0,ver_pubhash_check,0,ver_pubhash.length);
        System.arraycopy(check,0,ver_pubhash_check,ver_pubhash.length,check.length);

        //base58编码
        String addr = Base58.encode(ver_pubhash_check);
        System.out.println(addr);

        return Base58.encode(ver_pubhash_check);
    }

    /**
     * 用于校验地址是否符合规范，并返回校验结果
     * @param addr 要校验的地址
     * @return 校验结果ture符合规范，false不符合规范
     */
    public boolean isValidata(String addr){
        if ("".equals(addr)|| addr==null){
            return  false;
        }
        //base58解码
        byte[] ver_pubhash_check=Base58.decode(addr);
        if (ver_pubhash_check.length<4){
            return false;
        }
        //先申请存储校验的字节空间
        byte[] check=new byte[4];
        System.arraycopy(ver_pubhash_check,ver_pubhash_check.length-4,
                check,0,check.length);
        byte[] ver_pubhash = new byte[ver_pubhash_check.length-check.length];
        System.arraycopy(ver_pubhash_check,0,ver_pubhash,0,ver_pubhash.length);
        //双hash
        byte[] hash1=Hash.SHA256(ver_pubhash);
        byte[] hash2=Hash.SHA256(hash1);
        byte[] check_sum=new byte[4];
        System.arraycopy(hash2,0,check_sum,0,check_sum.length);
        Arrays.equals(check,check_sum);
        return Arrays.equals(check,check_sum);

    }


    /**
     * 该方法用于拼接生成原始格式的公钥
     *
     * @return原始公钥字节数组
     */
    public byte[] toUnCompressPubKey(ECPublicKey publicKey) {
        //曲线上的点
        ECPoint point = publicKey.getW();
        //大整数转字节数组
        byte[] xBytes = point.getAffineX().toByteArray();
        byte[] YBytes = point.getAffineY().toByteArray();

        //底层原理问题 [1,33)java会0字节在前面补一个字节
        //申请65字节大小的字节数组
        byte[] pubKey = new byte[1 + 2 * 32];
        pubKey[0] = 04;
        if (xBytes.length == 32) {
            System.arraycopy(xBytes, 0, pubKey, 1, xBytes.length);
        } else if (xBytes.length > 32) {
            System.arraycopy(xBytes, 1, pubKey, 1, xBytes.length - 1);
        }
        if (YBytes.length==32){
            System.arraycopy(YBytes,0,pubKey,33,YBytes.length);
        }else if (YBytes.length>32){
            System.arraycopy(YBytes,0,pubKey,33,32);
        }
        //将65字节的数据返回
        return pubKey;
    }

}